window.onload = function () {
  let str:string = `hello`
  console.log(str);
  let a1:string|number

  // 接口
  interface IPerson {
    id: number            // 确定属性
    name?: string         // 可选属性
    [xxx:string]: any     // 任意属性
  }
  let p1: IPerson = { id: 1, name: 'Jelly', age: 18 }
  let p1s: IPerson[] = [
    { id: 1, name: 'Jelly', age: 18 },
    { id: 2, name: 'Hisun', age: 20 },
  ]
  let p2s: Array<IPerson> = [
    { id: 1, name: 'Jelly', age: 18 },
    { id: 2, name: 'Hisun', age: 20 },
  ]
  
  // 函数
  function f1(n1:number, n2:number):boolean {
    return n1 === n2
  }
  f1(2, 3)
  // 表达式
  let f2 = function (x:number, y?:number):void {

  }
  f2(2, 3)
  // 接口应用场景 - 函数中的参数
  interface IObj {
    key: string
    value: number
  }
  const createObj = (obj: IObj): void => {
    console.log(`key: ${obj.key}, value: ${obj.value}`);
  }
  const sun = { key: 'sun', value: 18 }
  createObj(sun)

  // 接口应用场景 - 函数中的输入类型和输出类型
  const sum1 = (n1:number, n2:number) => (n1 + n2)
  sum1(2, 3)
  interface ISum {
    (n1:number, n2:number): number
  }
  const sum2: ISum = (n1, n2) => (n1 + n2)
  sum2(2, 3)

  // 接口应用场景 - 定义对象
  interface IO6 {
    id: number
    name: string
    dance(v:number):number
    say():void
  }
  const o6:IO6 = {
    id: 1,
    name: 'woshi o6',
    dance(v) {
      return v
    },
    say () {
      console.log(this.name);
      return 1
    }
  }
  o6.say()
  // 接口的继承(支持多接口继承)
  interface IHobby{
    hobby?(): void
  }
  interface Employee extends IPerson, IHobby {
    job: string
    idcard: number
  }
  const employee1:Employee = {
    id: 1,
    name: '员工1',
    job: '大堂经理',
    idcard: 1245412,
  }

  // 泛型 - 泛型变量T, 表示任意类型 - 应用场景如下:
  // 定义函数, 函数传入参数, 返回值包含入参
  console.group('-----泛型')
  function f3 (n1:number, n2:number): number[] {
    return [n1, n2]
  }
  f3(2, 3)
  function f4<T>(n1:T, n2:T):T[] {
    return [n1, n2]
  }
  f4<number>(2, 3)
  f4<string>('nihao', 'shijie')
  // 泛型约束, 例如: 类型必须有 length 属性
  // extends在泛型里表示约束
  interface ILength {
    length: number // 确定属性
  }
  function f5<T extends ILength>(str:T):number {
    return str.length
  }
  f5<string>('hello')
  // 泛型接口
  // 公共点
  interface ICommonFn {
    <T>(a:T, b:T):boolean
  }
  // function f6 (v1:number, v2:number):boolean {
  //   return v1 == v2
  // }
  const f6:ICommonFn = (v1, v2) => v1 == v2
  f6<string>('10', '12')
  const f7:ICommonFn = (v1, v2) => {
    if (v1 > v2) {
      return true
    } else {
      return false
    }
  }
  f7<number>(10, 12)


  console.groupEnd()
  
  console.group('-----类')
  // 类的定义, 类中的属性一旦被使用了,就需要在类中声明
  // 类实现接口
  interface IAnimal {
    name: string
    age: number
    weight?: number
    height?: number
  }
  class Animal implements IAnimal {
    name: string;
    age: number;
    constructor (name:string, age:number) {
      // 构造函数
      this.name = name
      this.age = age
    }
    action () {
      console.log(`${this.name} action`);
    }
  }
  const an = new Animal('小明', 18)
  an.action()
  // js 构造函数 - 用来对比 ES6 class
  function Animal2 (name, age) {
    this.name = name
    this.age = age
  }
  // 原型上共有属性
  Animal2.prototype.say = function () {
    console.log(`${this.name} say`);
  }
  Animal2.prototype.action = function () {
    console.log(`${this.name} action`);
  }
  const an2 = new Animal2('小红', 18)
  an2.say()
  // 类的继承,以及修饰符
  // public 公共属性或者方法, 默认值
  // private 私有属性或者方法, 只能在本类内部使用
  // protected 受保护的, 可以在本类以及继承的子类的内部使用
  class A {
    private a: string
    protected p: string
    constructor() {
      this.a = 'a'
      this.p = '保护属性P'
    }
    public createA() {
      console.log('aaaaa');
    }
  }
  const aa1 = new A()
  // aa1.a // 无法访问私有属性和受保护属性
  class B extends A {
    b: string
    constructor() {
      super() // 子类调用父类的构造器
      this.b = 'b'
    }
    say () {
      console.log('子类访问: ', this.p);
    }
  }
  const bb1 = new B()
  bb1.createA()
  bb1.say()
  // bb1.a // 报错, 继承无法访问私有属性或方法

  console.groupEnd()

  console.group('-----类型断言')
  let count:any = 100
  let count2 = count  // 自动推断类型
  console.log(typeof count2) // number
  // 符号: <> 和 as
  let num = <number>count     // 类型断言
  let num2 = count as number  // 类型断言
  // 使用场景
  interface IOo {
    id: number
    title: string
  }
  const oo1 = {} as IOo
  oo1.id = 111
  oo1.title = 'title'

  console.groupEnd()

  console.group('-----修饰符')
  // infer 声明类型变量, 用于存储模式匹配中捕获的类型
  console.groupEnd()
}
